package com.googlecode.tracey.messageserver;

import com.googlecode.funclate.Model;
import com.googlecode.funclate.json.Json;
import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.Function1;
import com.googlecode.totallylazy.Predicate;
import com.googlecode.totallylazy.Sequence;
import com.googlecode.utterlyidle.Response;
import com.googlecode.utterlyidle.annotations.FormParam;
import com.googlecode.utterlyidle.annotations.GET;
import com.googlecode.utterlyidle.annotations.POST;
import com.googlecode.utterlyidle.annotations.Path;
import com.googlecode.utterlyidle.annotations.Produces;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.googlecode.funclate.Model.model;
import static com.googlecode.totallylazy.Bytes.bytes;
import static com.googlecode.totallylazy.Closeables.using;
import static com.googlecode.totallylazy.Files.recursiveFiles;
import static com.googlecode.totallylazy.Predicates.equalTo;
import static com.googlecode.totallylazy.Predicates.is;
import static com.googlecode.totallylazy.Predicates.where;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.Strings.endsWith;
import static com.googlecode.utterlyidle.HttpHeaders.CONTENT_TYPE;
import static com.googlecode.utterlyidle.MediaType.APPLICATION_JSON;
import static com.googlecode.utterlyidle.MediaType.TEXT_HTML;
import static com.googlecode.utterlyidle.ResponseBuilder.response;
import static com.googlecode.utterlyidle.Status.OK;

@Path("/messages")
public class MessagesResource {
    private final LogDirectory logDirectory;

    public MessagesResource(LogDirectory logDirectory) {
        this.logDirectory = logDirectory;
    }


    @GET
    @Produces(TEXT_HTML)
    public Response searchForms() {
        return response().status(OK).header(CONTENT_TYPE, TEXT_HTML).entity(bytes(getClass().getResourceAsStream("searchForms.html"))).build();
    }


    @POST
    @Produces(APPLICATION_JSON)
    public String searchByTraceyId(@FormParam("traceyId") String traceyId) {
        final Sequence<Model> messages = recursiveFiles(new File(logDirectory.value())).flatMap(messages(withTraceyId(traceyId)));

        return model()
                .add("messages", messages.toList())
                .toString();
    }

    private Predicate<Model> withTraceyId(String traceyId) {
        return where(Model.value("traceyId", String.class), is(equalTo(traceyId)));
    }

    private Callable1<? super File, ? extends Iterable<? extends Model>> messages(final Predicate<Model> where) {
        return new Callable1<File, Iterable<? extends Model>>() {
            public Iterable<? extends Model> call(File file) throws Exception {
                return extractMessages(file)
                        .map(jsonToModel())
                        .filter(where);
            }

            private Sequence<String> extractMessages(final File file) throws IOException {
                return chunkify(file, endsWith("begin-json"), endsWith("end-json"));

            }

            private Function1<String, Model> jsonToModel() {
                return new Function1<String, Model>() {
                    public Model call(String s) throws Exception {
                        return Model.fromMap(Json.parse(s));
                    }
                };
            }
        };
    }

    private Sequence<String> chunkify(File file, final Predicate<String> beginPredicate, final Predicate<String> endPredicate) throws IOException {
        return using(new BufferedReader(new FileReader(file)), new Callable1<BufferedReader, Sequence<String>>() {
            public Sequence<String> call(BufferedReader bufferedReader) throws Exception {
                List<String> result = new ArrayList<String>();
                StringBuffer currentChunk = null;
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    if (currentChunk == null && beginPredicate.matches(line)) {
                        currentChunk = new StringBuffer();
                    } else if (currentChunk != null & endPredicate.matches(line)) {
                        result.add(currentChunk.toString());
                        currentChunk = null;
                    } else if (currentChunk != null) {
                        currentChunk.append(line);
                    }
                }
                return sequence(result);
            }
        });
    }


}
